﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;


namespace Taype.Trading.BTC.Utilities
{
    
    /// <summary>
    /// TradeHillNetTools related utilities
    /// </summary>
    class TradeHillNetTools
    {
        string UserAgent = "TAYPE International/0.1 TradeHill API Client"; // "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"

        /// <summary>
        /// Perform an authenticated post to TradeHill client API
        /// </summary>
        /// <param name="url">API URL to post to</param>
        /// <param name="name">Application API username</param>
        /// <param name="pass">Application API password</param>
        /// <param name="moreargs">DoAuthenticatedAPIPost parameters</param>
        /// <returns></returns>
        public string DoAuthenticatedAPIPost(string url, string name, string pass, string moreargs = null)
        {
            // Verify client was instantiated with username and password
            if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass))
                throw new ArgumentException("Cannot call private api's without api key and secret");
            var parameters = "name=" + name + "&pass=" + pass;
            if (!string.IsNullOrEmpty(moreargs))
                parameters += "&" + moreargs;
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
            webRequest.ContentType = "application/x-www-form-urlencoded";
            webRequest.Method = "POST";
            webRequest.UserAgent = UserAgent;
            webRequest.Accept = "application/json";
            byte[] byteArray = Encoding.UTF8.GetBytes(parameters);
            webRequest.ContentLength = byteArray.Length;
            using (Stream dataStream = webRequest.GetRequestStream())
            {
                dataStream.Write(byteArray, 0, byteArray.Length);
            }
            using (WebResponse webResponse = webRequest.GetResponse())
            {
                using (Stream str = webResponse.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(str))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }
        }

        /// <summary>
        /// Perform an authenticated post to TradeHill client API
        /// </summary>
        /// <param name="url">API URL to post to</param>
        /// <param name="moreargs">DoUnauthenticatedAPIPost parameters</param>
        /// <returns></returns>
        public string DoUnauthenticatedAPIPost(string url, string moreargs = null)
        {
            var parameters = "";
            if (!string.IsNullOrEmpty(moreargs))
                parameters += "&" + moreargs;
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
            webRequest.ContentType = "application/x-www-form-urlencoded";
            webRequest.Method = "POST";
            webRequest.UserAgent = UserAgent;
            webRequest.Accept = "application/json";
            byte[] byteArray = Encoding.UTF8.GetBytes(parameters);
            webRequest.ContentLength = byteArray.Length;
            using (Stream dataStream = webRequest.GetRequestStream())
            {
                dataStream.Write(byteArray, 0, byteArray.Length);
            }
            using (WebResponse webResponse = webRequest.GetResponse())
            {
                using (Stream str = webResponse.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(str))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }
        }
    }

    /// <summary>
    /// Utility class to parse JSON and return .NET objects
    /// http://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object/3806407#3806407
    ///{
    ///"Items":[
    ///{ "Name":"Apple", "price":12.3 },
    ///{ "Name":"Grape", "price":3.21 }
    ///],
    ///"date":"21/11/2010"
    ///}
    ///
    /// string json = jsonDataStr;
    /// var serializer = new JavaScriptSerializer();
    /// serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
    /// dynamic data = serializer.Deserialize(json, typeof(object));
    ///data.date; // "21/11/2010"
    ///data.Items.Count; // 2
    ///data.Items[0].Name; // "Apple"
    ///data.Items[0].price; // 12.3 (as b decimal)
    ///data.Items[1].Name; // "Grape"
    ///data.Items[1].price; // 3.21 (as b decimal)
    ///
    /// </summary>
    sealed class DynamicJsonConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
        }

        private sealed class DynamicJsonObject : DynamicObject
        {
            private readonly IDictionary<string, object> _dictionary;

            public DynamicJsonObject(IDictionary<string, object> dictionary)
            {
                if (dictionary == null)
                    throw new ArgumentNullException("dictionary");
                _dictionary = dictionary;
            }

            public override string ToString()
            {
                var sb = new StringBuilder("{");
                ToString(sb);
                return sb.ToString();
            }

            private void ToString(StringBuilder sb)
            {
                var firstInDictionary = true;
                foreach (var pair in _dictionary)
                {
                    if (!firstInDictionary)
                        sb.Append(",");
                    firstInDictionary = false;
                    var value = pair.Value;
                    var name = pair.Key;
                    if (value is string)
                    {
                        sb.AppendFormat("{0}:\"{1}\"", name, value);
                    }
                    else if (value is IDictionary<string, object>)
                    {
                        new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
                    }
                    else if (value is ArrayList)
                    {
                        sb.Append(name + ":[");
                        var firstInArray = true;
                        foreach (var arrayValue in (ArrayList)value)
                        {
                            if (!firstInArray)
                                sb.Append(",");
                            firstInArray = false;
                            if (arrayValue is IDictionary<string, object>)
                                new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
                            else if (arrayValue is string)
                                sb.AppendFormat("\"{0}\"", arrayValue);
                            else
                                sb.AppendFormat("{0}", arrayValue);

                        }
                        sb.Append("]");
                    }
                    else
                    {
                        sb.AppendFormat("{0}:{1}", name, value);
                    }
                }
                sb.Append("}");
            }

            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                if (!_dictionary.TryGetValue(binder.Name, out result))
                {
                    // return null to avoid exception.  caller can check for null this way...
                    result = null;
                    return true;
                }

                var dictionary = result as IDictionary<string, object>;
                if (dictionary != null)
                {
                    result = new DynamicJsonObject(dictionary);
                    return true;
                }

                var arrayList = result as ArrayList;
                if (arrayList != null && arrayList.Count > 0)
                {
                    if (arrayList[0] is IDictionary<string, object>)
                        result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
                    else
                        result = new List<object>(arrayList.Cast<object>());
                }

                return true;
            }
        }

    }

}
